package org.nox.helloseries.core.services.recherche.impl;

import java.util.ArrayList;
import java.util.List;

import org.apache.commons.collections.CollectionUtils;
import org.nox.helloseries.configuration.CustomConfiguration;
import org.nox.helloseries.configuration.CustomConfigurationKey;
import org.nox.helloseries.core.connector.tracker.AbstractRSSTorrentSearch;
import org.nox.helloseries.core.connector.tracker.impl.ExtraTorrentSearch;
import org.nox.helloseries.core.connector.tracker.impl.EztvSearch;
import org.nox.helloseries.core.connector.tracker.impl.FrenchTorrentDBSearch;
import org.nox.helloseries.core.connector.tracker.impl.Torrent411TorrentSearch;
import org.nox.helloseries.core.connector.tracker.impl.TorrentZapSearch;
import org.nox.helloseries.core.exceptions.TrackerSearchException;
import org.nox.helloseries.core.services.configuration.serie.SerieConfigurationService;
import org.nox.helloseries.core.services.configuration.tracker.TrackerConfigurationService;
import org.nox.helloseries.core.services.recherche.TrackerSearchService;
import org.nox.helloseries.core.services.recherche.domain.RetourRechercheTracker;
import org.nox.helloseries.domain.Serie;
import org.nox.helloseries.domain.SerieFileInformation;
import org.nox.helloseries.domain.Tracker;
import org.nox.helloseries.factory.HelloSeriesServiceFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class DefaultTrackerSearchService implements TrackerSearchService
{
    private static final Logger LOG = LoggerFactory.getLogger( DefaultTrackerSearchService.class );

    private TrackerConfigurationService trackerConfigurationService = HelloSeriesServiceFactory.createOrGetService( TrackerConfigurationService.class );

    private SerieConfigurationService serieConfigurationService = HelloSeriesServiceFactory.createOrGetService( SerieConfigurationService.class );

    // Property de conf
    private static Double MIN_FILE_SIZE = CustomConfiguration.get().getDouble( CustomConfigurationKey.MIN_FILE_SIZE );

    private static Double MAX_FILE_SIZE = CustomConfiguration.get().getDouble( CustomConfigurationKey.MAX_FILE_SIZE );

    private static Integer MIN_SEEDS = CustomConfiguration.get().getInteger( CustomConfigurationKey.MIN_SEEDS );

    @Override
    public RetourRechercheTracker searchAll()
    {
        LOG.debug( "Début recherche sur trackers torrents" );
        List<Serie> allSeriesToBeDownloaded = new ArrayList<>();

        // Récupération de la liste des séries configurées
        List<Serie> seriesARechercher = serieConfigurationService.getSeriesActives();

        // Récupération de la liste des trackers configurés
        for ( Tracker tracker : trackerConfigurationService.getTrackersActifs() )
        {

            LOG.info( "Recherche sur le tracker " + tracker.getNomTracker() );
            try
            {

                List<Serie> seriesToBeDownloadedForTracker = new ArrayList<>();
                // Pour chaque série, on recherche sur le tracker correspondant
                for ( Serie serieARechercher : seriesARechercher )
                {
                    LOG.info( "Recherche de la série {}", serieARechercher.getNom() );
                    // Mémorisation
                    seriesToBeDownloadedForTracker.addAll( getSeriesToBeDownloaded( serieARechercher, tracker ) );
                }
                // Mémorisation du retour
                allSeriesToBeDownloaded.addAll( seriesToBeDownloadedForTracker );
            }
            catch ( TrackerSearchException e )
            {
                LOG.error( "Impossible de récuperer les données sur le tracker " + tracker.getNomTracker(), e );
            }
        }
        LOG.info( "Fin recherche sur trackers torrents" );
        LOG.info( allSeriesToBeDownloaded.size() + " séries à télécharger" );

        // Création de l'objet Retour
        RetourRechercheTracker retour = new RetourRechercheTracker();
        retour.setSeriesARechercherUpdated( seriesARechercher );
        retour.setSeriesToBeDownloaded( allSeriesToBeDownloaded );
        return retour;
    }

    @Override
    public List<Serie> getSeriesToBeDownloaded( Serie serieARechercher, Tracker tracker ) throws TrackerSearchException
    {
        return getSeriesToBeDownloaded( serieARechercher, tracker, true );
    }

    @Override
    public Serie getSerieToBeDownloaded( Serie serieARechercher, Tracker tracker ) throws TrackerSearchException
    {
        // Récupération de la liste des séries dispos sur le tracker
        LOG.debug( "Récupération des séries disponibles pour {} sur {}", serieARechercher, tracker.getNomTracker() );
        return getSerie( serieARechercher, getSeriesFromTracker( serieARechercher, tracker ) );
    }

    /**
     * @param serieARechercher
     * @param tracker
     * @param startByIncEpisode
     * flag permettant de savoir si on a déjà trouvé une série dans la saison courante
     * @return
     * @throws TrackerSearchException
     */
    private List<Serie> getSeriesToBeDownloaded( Serie serieARechercher, Tracker tracker, boolean startByIncEpisode ) throws TrackerSearchException
    {
        List<Serie> seriesToBeDownloaded = new ArrayList<>();

        Serie serieARechercherClone = serieARechercher.clone();
        if ( startByIncEpisode )
        {
            serieARechercherClone.incEpisode();
        }
        else
        {
            serieARechercherClone.newSaison();
        }

        // Récupération de la liste des séries dispos sur le tracker
        LOG.debug( "Récupération des séries disponibles pour {} sur {}", serieARechercherClone, tracker.getNomTracker() );

        List<Serie> resultatTracker = getSeriesFromTracker( serieARechercherClone, tracker );

        // Création serie temporaire pour max seed
        Serie maxSeededSerie = getSerie( serieARechercherClone, resultatTracker );

        if ( maxSeededSerie != null )
        {
            startByIncEpisode = true;
        }

        // Si la série a été trouvé dans la saison courante mais que
        // potentiellement c'est le dernier ep on test la prochaine saison
        if ( maxSeededSerie == null && startByIncEpisode )
        {
            if ( CollectionUtils.isEmpty( seriesToBeDownloaded ) )
            {
                LOG.debug( "Aucun résultat pour la saison courante" );
            }
            else
            {
                LOG.debug( "Plus aucun résultat pour la saison courante" );
            }

            LOG.debug( "Recherche d'une nouvelle saison" );
            seriesToBeDownloaded.addAll( getSeriesToBeDownloaded( serieARechercher, tracker, false ) );
        }
        // Si on a trouvé une série on la mémorise, et on relance la recherche
        // dans la même saison
        else if ( maxSeededSerie != null )
        {
            LOG.debug( "Série " + maxSeededSerie + " trouvée (url=" + maxSeededSerie.getSerieFileInformation().getUrl() + ")" );

            // mise à niveau du curseur de la série recherchée
            serieARechercher.setEpisode( maxSeededSerie.getEpisode() );
            serieARechercher.setSaison( maxSeededSerie.getSaison() );

            seriesToBeDownloaded.add( maxSeededSerie );

            LOG.debug( "Recherche de l'épisode suivant" );
            // Lancement de la recherche
            seriesToBeDownloaded.addAll( getSeriesToBeDownloaded( serieARechercher, tracker, true ) );
        }
        else
        {
            LOG.debug( "Plus aucun résultat pour la série" );
        }

        return seriesToBeDownloaded;
    }

    /**
     * Cherche dans les résultats le meilleur résultat pour la série à rechercher
     * 
     * @param serieARechercher
     * @param resultatTracker
     * @return
     */
    private Serie getSerie( Serie serieARechercher, List<Serie> resultatTracker )
    {
        Serie maxSeededSerie = null;
        for ( Serie serieResultat : resultatTracker )
        {
            // Vérif série trouvée correspondante
            if ( serieResultat.equals( serieARechercher ) )
            {
                // Verif seeds taille en fonction de la conf
                if ( maxSeededSerie == null || checkParametresConf( serieResultat ) )
                {
                    maxSeededSerie = serieResultat;
                }
            }
        }
        return maxSeededSerie;
    }

    /**
     * Verif seeds taille en fonction de la conf
     * 
     * @param serieResultat
     * @return
     */
    private boolean checkParametresConf( Serie serieResultat )
    {
        SerieFileInformation serieFileInformation = serieResultat.getSerieFileInformation();
        boolean checkSeeds = ( serieFileInformation.getSeeds() >= MIN_SEEDS || serieFileInformation.getSeeds() == -1 );
        boolean checkSize = serieFileInformation.getTailleFichier() >= MIN_FILE_SIZE && serieFileInformation.getTailleFichier() <= MAX_FILE_SIZE;
        return checkSeeds && checkSize;
    }

    private List<Serie> getSeriesFromTracker( Serie serieARechercher, Tracker tracker ) throws TrackerSearchException
    {
        return getSearchStrategy( tracker ).executeSearch( serieARechercher );
    }

    /**
     * Retourne la strategy de recherche en fonction du tracker
     * 
     * @param tracker
     * @return
     */
    protected AbstractRSSTorrentSearch getSearchStrategy( Tracker tracker )
    {
        switch ( tracker.getNomTracker() )
        {
        case FRENCHTORRENTDB:
            return new FrenchTorrentDBSearch();
        case EZTV:
            return new EztvSearch();
        case TORRENTZAP:
            return new TorrentZapSearch();
        case EXTRATORRENT:
            return new ExtraTorrentSearch();
        case TORRENT411:
            return new Torrent411TorrentSearch();
        default:
            return null;
        }
    }
}
